package com.mashuai.utils.toolkit.core.msValidation;

import com.mashuai.utils.base.MsAopUtils;
import com.mashuai.utils.base.MsAssertUtils;
import com.mashuai.utils.base.MsEmptyUtils;
import com.mashuai.utils.toolkit.core.msConverts.MsConvertDataUtils;
import com.mashuai.utils.toolkit.core.msValidation.annotation.MsCheckObjParams;
import com.mashuai.utils.toolkit.core.msValidation.annotation.MsCheckParams;
import org.apache.commons.collections4.MapUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;

@Aspect
@Component
public class MsCheckParamAop {

    private static final String NO_CHECK_BASIC_TYPE = "待校验的参数是基本数据类型，请保证校验的参数是{}结构";
    private static final String FORMAT_ERROR = "使用@MsCheckObjParams注解进行参数校验时，必须保证所检验的参数是{}的json格式";
    private static final String PARAMS_NOT_EMPTY = "参数值缺失，请联系管理员";

    /**
     * 声明一个切入点，范围为controller包下所有的类
     * 注：作为切入点签名的方法必须返回void类型
     */
    @Pointcut("@annotation(com.mashuai.utils.toolkit.core.msValidation.annotation.MsCheckObjParams) || @annotation(com.mashuai.utils.toolkit.core.msValidation.annotation.MsCheckParams)")
    private void pointcut() {
    }

    /**
     * 前置通知：在某连接点之前执行的通知，但这个通知不能阻止连接点之前的执行流程（除非它抛出一个异常）
     */

    @Before("pointcut()")
    public void doBefore(JoinPoint joinPoint) throws Exception {
        //获取目标方法的参数信息
        Object[] args = joinPoint.getArgs();
        //优先检查@MsCheckParams
        MsCheckParams annotationA = MsAopUtils.getAnnotation(joinPoint, MsCheckParams.class);
        if (annotationA != null) {
            String methodPath = MsAopUtils.getClass(joinPoint).getName() + "." + MsAopUtils.getMethod(joinPoint).getName();
            checkParams(args, annotationA, methodPath);
        }
        //判断是否需要检查入参
        MsCheckObjParams annotationB = MsAopUtils.getAnnotation(joinPoint, MsCheckObjParams.class);
        if (annotationB != null) {
            checkObjParams(args, annotationB);
        }
    }


    private void checkObjParams(Object[] args, MsCheckObjParams annotation) {
        String[] keys = annotation.keys();
        String errorMsg = annotation.message();
        int index = annotation.index();
        Object arg = args[index];
        if (arg == null) MsAssertUtils.throwException(PARAMS_NOT_EMPTY);
        if (MsConvertDataUtils.paramIsBasicType(arg)) MsAssertUtils.throwException(NO_CHECK_BASIC_TYPE);
        //定位到校验的参数
        Map<String, Object> map = null;
        try {
            map = MsConvertDataUtils.beanToMap(arg);
        } catch (Exception e) {
            MsAssertUtils.throwInnerException(FORMAT_ERROR);
        }
        if (keys.length == 0) return;
        List<String> msgList = new ArrayList<>();
        for (String key : keys) {
            Object val = MapUtils.getObject(map, key);
            if (val == null || MsEmptyUtils.isEmpty(String.valueOf(val))) {
                msgList.add("必填参数【" + key + "】值为空");
            }
        }
        //优先抛出自定义异常
        if (msgList.size() != 0) {
            MsAssertUtils.throwException(MsEmptyUtils.isNotEmpty(errorMsg) ? errorMsg : msgList.stream().collect(Collectors.joining(" ; ")));
        }
    }

    private void checkParams(Object[] args, MsCheckParams objA, String methodPath) {
        int[] excludeIndex = objA.excludeIndex();
        Set<Integer> excludeSet = Arrays.stream(excludeIndex).boxed().collect(Collectors.toSet());
        for (int i = 0; i < args.length; i++) {
            if (!excludeSet.contains(i)) {
                //如果方法就一个参数时
                if (args.length == 1) {
                    MsAssertUtils.objIsNull(args[i], MsAssertUtils.MSG);
                } else {
                    MsAssertUtils.objIsNull(args[i], methodPath + "() 方法中index = [" + i + "]位置上的参数值为空");
                }
            }
        }
    }
}